package com.carryon.modules.sj;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.List;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.carryon.lang.MapJ;
import com.carryon.modules.sj.execute.PrimitiveExecute;
import com.carryon.modules.sj.execute.SJExecute;
import com.carryon.modules.sj.execute.StaticMethodExcecute;

/**
 * @author Helin
 * @mail hl_0074@sina.com
 * @date 2011-9-27
 * 
 */
public class Handler extends Thread {

	private static Log log = LogFactory.getLog(Handler.class);

	private Class<? extends Object> clazz;
	private String method;
	private HttpServletResponse response;
	private HttpServletRequest request;
	private boolean wait;

	@SuppressWarnings("unchecked")
	@Override
	public void run() {

		SJ sj = new SJ();
		Execute execute = null;

		if (isStaticMethod(this.clazz, this.method)) {
			execute = new StaticMethodExcecute(this.clazz, this.method);
		} else {
			if (SJ.class.isAssignableFrom(clazz)) {
				log.debug("SJ class:" + this.clazz.getCanonicalName() + " ,find method :" + this.method);

				try {
					sj = (SJ) this.clazz.newInstance();
				} catch (Exception e) {
					log.error("instance SJ error：" + e.getMessage());
				}
				execute = new SJExecute(this.method);
			} else {
				log.debug(this.clazz.getCanonicalName() + " is not a SJ class ,try execute normal class's method:" + this.method);
				execute = new PrimitiveExecute(this.clazz, this.method);
			}
		}

		MapJ param = MapJ.mapRequest(request.getParameterMap());
		for (String key : param.keySet()) {
			sj.getRequest().put(key, param.get(key));
		}

		for (String attr : (List<String>) Collections.list(request.getSession().getAttributeNames())) {
			sj.getSession().put(attr, request.getSession().getAttribute(attr));
		}

		Cookie[] cookies = request.getCookies();
		for (Cookie cook : cookies) {
			sj.getCookie().put(cook.getName(), cook.getValue());
		}
		try {
			execute.execute(sj);
		} catch (Exception e) {
			sj.status(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
			sj.errorMessage(e.getMessage());
			log.error("execute fail", e);
		}

		if (wait) {
			this.response.setContentType("text/plain");
			this.response.setCharacterEncoding("UTF-8");
			try {
				this.response.getWriter().write(sj.getResponse().toJSON());
			} catch (IOException e) {
			}
		}
	}

	private boolean isStaticMethod(Class<? extends Object> clazz, String method) {

		for (Method m : clazz.getMethods()) {
			if (m.getName().equals(method)) {
				if (Modifier.isStatic(m.getModifiers())) {
					return true;
				}
			}
		}

		return false;
	}

	public void setWait(boolean wait) {

		this.wait = wait;
	}

	public static void handle(HttpServletRequest request, HttpServletResponse resp) {

		resp.setHeader("Cache-Control", "no-cache");
		resp.setHeader("Pragma", "no-cache");
		resp.setHeader("Expires", "0");
		try {
			request.setCharacterEncoding("UTF-8");
		} catch (UnsupportedEncodingException e2) {
		}

		String m = request.getParameter("method");
		if (StringUtils.isBlank(m) || !m.matches("([\\w$_]\\.?)*([\\w$_]+)")) {
			log.error("not a valide request");
			return;
		}

		Handler handler = new Handler();

		String claz = m.substring(0, m.lastIndexOf("."));
		String methodName = m.substring(m.lastIndexOf(".") + 1);
		handler.setResponse(resp);
		handler.setRequest(request);

		Class<? extends Object> clazz = null;
		try {
			clazz = Class.forName(claz);
		} catch (ClassNotFoundException e) {
			log.error(e);
			Response r = new Response();
			r.status(500);
			r.message("class not found:" + claz);
			try {
				resp.getWriter().write(r.toJSON());
			} catch (IOException e1) {
			}
			return;
		}

		handler.setClazz(clazz);
		handler.setMethod(methodName);

		if (StringUtils.equals("true", request.getParameter("nowait"))) {
			handler.start();
		} else {
			handler.setWait(true);
			handler.run();
		}
	}

	public void setClazz(Class<? extends Object> clazz) {

		this.clazz = clazz;
	}

	public void setMethod(String method) {

		this.method = method;
	}

	public void setResponse(HttpServletResponse response) {

		this.response = response;
	}

	public void setRequest(HttpServletRequest request) {

		this.request = request;
	}

}
